package ace.module.account.core.impl.manager.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.core.utils.SystemClockUtils;
import ace.cmp.id.generator.api.LongIdGenerator;
import ace.cmp.util.AceEnumUtils;
import ace.module.account.api.enums.IamAccountProfileRegisterSourceFromEnum;
import ace.module.account.api.enums.IamAccountProfileSexEnum;
import ace.module.account.api.model.dto.IamAccountProfileDto;
import ace.module.account.api.model.input.IamAccountCommonInfoInput;
import ace.module.account.core.impl.dao.entity.IamAccount;
import ace.module.account.core.impl.dao.entity.IamAccountProfile;
import ace.module.account.core.impl.dao.entity.QIamAccountProfile;
import ace.module.account.core.impl.dao.repository.IamAccountProfileRepository;
import ace.module.account.core.impl.manager.IamAccountProfileManager;
import ace.module.common.core.impl.manager.impl.AbstractEntityOfAppManager;
import com.querydsl.core.types.ExpressionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * .
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class IamAccountProfileManagerImpl
    extends AbstractEntityOfAppManager<IamAccountProfile, Long, IamAccountProfileDto>
    implements IamAccountProfileManager {

  private final IamAccountProfileRepository iamAccountProfileRepository;
  private final LongIdGenerator idGenerator;

  @Override
  public IamAccountProfileDto getDtoByAccountIdAndDeleteFlagIsFalse(Long accountId) {
    IamAccountProfile iamAccountProfile = this.getByAccountIdAndDeleteFlagIsFalse(accountId);
    IamAccountProfileDto dto = this.getEntityToDtoConverter().convert(iamAccountProfile);
    return dto;
  }

  @Override
  public IamAccountProfile getByAccountIdAndDeleteFlagIsFalse(Long accountId) {
    return iamAccountProfileRepository.getByIamAccountIdAndDeleteFlag(
        accountId, CoreConstant.DELETE_FLAG_NOT);
  }

  @Override
  public Map<Long, IamAccountProfileDto> getAccountIdAndProfileDtoMapByAccountId(List<Long> iamAccountIdList) {
    return this.getDtoByAccountId(iamAccountIdList)
        .stream()
        .collect(HashMap::new,
            (m, entry) -> {
              m.put(entry.getIamAccountId(), entry);
            },
            HashMap::putAll);
  }

  @Override
  public List<IamAccountProfileDto> getDtoByAccountId(List<Long> iamAccountIdList) {
    return this.getByAccountId(iamAccountIdList)
        .stream()
        .map(p -> this.getEntityToDtoConverter().convert(p))
        .collect(Collectors.toList());
  }

  @Override
  public List<IamAccountProfile> getByAccountId(List<Long> iamAccountIdList) {
    QIamAccountProfile qIamAccountProfile = QIamAccountProfile.iamAccountProfile;
    return Stream.ofNullable(iamAccountIdList)
        .map(accountIds -> this.iamAccountProfileRepository.query(
                sqlQuery -> sqlQuery
                    .select(qIamAccountProfile)
                    .from(qIamAccountProfile)
                    .where(ExpressionUtils.allOf(
                        qIamAccountProfile.iamAccountId.in(accountIds),
                        qIamAccountProfile.deleteFlag.eq(CoreConstant.DELETE_FLAG_NOT)
                    ))
                    .fetch()
            )
        )
        .flatMap(List::stream)
        .collect(Collectors.toList());

  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public void insertOrUpdate(Long accountId, Long iamAppId, IamAccountCommonInfoInput common) {
    IamAccountProfile iamAccountProfile = this.getByAccountIdAndDeleteFlagIsFalse(accountId);
    boolean isNew = Objects.isNull(iamAccountProfile);
    if (isNew) {
      iamAccountProfile = new IamAccountProfile();
      iamAccountProfile.setId(idGenerator.get());
      iamAccountProfile.setCreateTime(SystemClockUtils.currentTimeMillis());
      iamAccountProfile.setDeleteFlag(CoreConstant.DELETE_FLAG_NOT);
      iamAccountProfile.setRowVersion(CoreConstant.DEFAULT_ROW_VERSION);
    }

    iamAccountProfile.setIamAccountId(accountId);
    iamAccountProfile.setIamAppId(iamAppId);
    iamAccountProfile.setAvatar(common.getAvatar());
    iamAccountProfile.setBirthday(common.getBirthday());
    iamAccountProfile.setUpdateTime(SystemClockUtils.currentTimeMillis());
    iamAccountProfile.setNickName(common.getNickName());
    iamAccountProfile.setRealName(common.getRealName());
    iamAccountProfile.setSex(common.getSex());
    iamAccountProfile.setSignature(common.getSignature());
    iamAccountProfile.setRegisterSourceFrom(common.getRegisterSourceFrom());

    if (isNew) {
      this.iamAccountProfileRepository.insert(iamAccountProfile, false);
    } else {
      this.iamAccountProfileRepository.updateById(iamAccountProfile, false);
    }
  }

  @Transactional(rollbackFor = Throwable.class)
  @Override
  public void addOrModify(IamAccount iamAccount,
      String nickName,
      String avatar,
      String realName,
      Integer sex,
      Long birthday,
      String signature,
      String contactInfo,
      Integer registerSourceFrom
  ) {
    IamAccountProfile oldIamAccountProfile = this.getByAccountIdAndDeleteFlagIsFalse(iamAccount.getId());
    IamAccountProfileSexEnum sexEnum = AceEnumUtils.getEnum(IamAccountProfileSexEnum.class, sex);
    IamAccountProfileRegisterSourceFromEnum registerSourceFromEnum = AceEnumUtils.getEnum(IamAccountProfileRegisterSourceFromEnum.class, registerSourceFrom);

    IamAccountProfile profile = oldIamAccountProfile;
    if (oldIamAccountProfile == null) {
      profile = new IamAccountProfile();
      profile.setId(this.getNewId());
      profile.setIamAccountId(iamAccount.getId());
      profile.setIamAppId(iamAccount.getIamAppId());
    }
    profile.setNickName(nickName);
    profile.setAvatar(avatar);
    profile.setRealName(realName);
    profile.setBirthday(birthday);
    profile.setSignature(signature);
    profile.setContactInfo(contactInfo);
    profile.setSex(sexEnum.getCode());
    profile.setRegisterSourceFrom(registerSourceFromEnum.getCode());

    this.save(profile, false);
  }
}
